package com.bestv.search.spider.connection.impl;

import com.bestv.search.spider.connection.IConnection;
import com.bestv.search.spider.util.SpiderConst;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;

public class HttpClientConnection implements IConnection {

    // [Start] fields

    private final Log log = LogFactory.getLog(HttpClientConnection.class);
    private final int defaultConnectTimeOut = 10000;
    private final int defaultReadTimeOut = 60000;

    private HttpClient client;
    private HttpMethodBase method;

    // [end] fields

    // [Start] Constructor

    public HttpClientConnection() {
        this.client = new HttpClient();
    }

    // [End] Constructor

    // [Start] public method

    @Override
    public void connect(URL url) {
        connect(url.toString(), SpiderConst.POST);
    }

    @Override
    public void connect(URL url, String methodType) {
        if (methodType.equalsIgnoreCase(SpiderConst.GET)) {
            connect(url.toString(), SpiderConst.GET);
        } else {
            connect(url.toString(), SpiderConst.POST);
        }
    }

    @Override
    public void connect(String url) {
        connect(url, SpiderConst.POST);
    }

    @Override
    public void connect(String url, String methodType) {
        if (methodType.equalsIgnoreCase(SpiderConst.GET)) {
            method = new GetMethod(url);
        } else {
            method = new PostMethod(url);
        }
    }

    @Override
    public InputStream request() {
        return request(new byte[0]);
    }

    @Override
    public InputStream request(int connectTimeout, int readTimeout) {
        return request(new byte[0], connectTimeout, readTimeout);
    }

    @Override
    public InputStream request(byte[] out) {
        int httpStatus = executeMethod(out, defaultConnectTimeOut, defaultReadTimeOut);
        try {
            if (httpStatus == HttpStatus.SC_OK) {
                return method.getResponseBodyAsStream();
            }
        } catch (IOException e) {
            if (log.isErrorEnabled())
                log.error(e.getMessage());
        }
        return null;
    }

    @Override
    public InputStream request(byte[] out, int connectTimeout, int readTimeout) {
        int httpStatus = executeMethod(out, connectTimeout, readTimeout);
        try {
            if (httpStatus == HttpStatus.SC_OK) {
                return method.getResponseBodyAsStream();
            }
        } catch (IOException e) {
            if (log.isErrorEnabled())
                log.error(e.getMessage());
        }
        return null;
    }

    @Override
    public InputStream request(String out) {
        return request(out.getBytes());
    }

    @Override
    public InputStream request(String out, int connectTimeout, int readTimeout) {
        return request(out.getBytes(), connectTimeout, readTimeout);
    }

    @Override
    public InputStream request(NameValuePair[] pairs, int connectTimeout, int readTimeout) {
        int httpStatus = executeMethod(pairs, connectTimeout, readTimeout);
        try {
            if (httpStatus == HttpStatus.SC_OK) {
                return method.getResponseBodyAsStream();
            }
        } catch (IOException e) {
            if (log.isErrorEnabled())
                log.error(e.getMessage());
        }
        return null;
    }

    @Override
    public String requestStrResult(byte[] out) {
        int httpStatus = executeMethod(out, defaultConnectTimeOut, defaultReadTimeOut);
        try {
            if (httpStatus == HttpStatus.SC_OK) {
                return method.getResponseBodyAsString();
            }
        } catch (IOException e) {
            if (log.isErrorEnabled())
                log.error(e.getMessage());
        }
        return null;
    }

    @Override
    public String requestStrResult(String out) {
        return requestStrResult(out.getBytes());
    }

    public void release() {
        method.releaseConnection();
    }

    // [End] public method

    // [Start] private method

    private int executeMethod(byte[] out, int connectTimeout, int readTimeout) {
        int httpStatusCode = -1;
        try {
            RequestEntity entity = new ByteArrayRequestEntity(out);
            if (method instanceof PostMethod) {
                ((PostMethod) method).setRequestEntity(entity);
            }
            method.getParams()
                    .setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(0, false));
            method.setRequestHeader("Connection", "close");
            client.getParams().setBooleanParameter("http.protocol.expect-continue", false);
            client.getHttpConnectionManager().getParams().setConnectionTimeout(connectTimeout);
            client.getHttpConnectionManager().getParams().setSoTimeout(readTimeout);

            httpStatusCode = client.executeMethod(method);
        } catch (UnsupportedEncodingException e) {
            if (log.isErrorEnabled())
                log.error(e.getMessage());
        } catch (IOException e) {
            if (log.isErrorEnabled())
                log.error(e.getMessage());
        }

        return httpStatusCode;
    }

    private int executeMethod(NameValuePair[] pairs, int connectTimeout, int readTimeout) {
        int httpStatusCode = -1;
        try {
            if (method instanceof PostMethod) {
                ((PostMethod) method).setRequestBody(pairs);
            }
            method.getParams()
                    .setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(0, false));
            method.setRequestHeader("Connection", "close");
            client.getParams().setBooleanParameter("http.protocol.expect-continue", false);
            client.getHttpConnectionManager().getParams().setConnectionTimeout(connectTimeout);
            client.getHttpConnectionManager().getParams().setSoTimeout(readTimeout);

            httpStatusCode = client.executeMethod(method);
        } catch (UnsupportedEncodingException e) {
            if (log.isErrorEnabled())
                log.error(e.getMessage());
        } catch (IOException e) {
            if (log.isErrorEnabled())
                log.error(e.getMessage());
        }

        return httpStatusCode;
    }

    // [End] private method

    // [Start] Getter,Setter method

    public HttpClient getClient() {
        return client;
    }

    public void setClient(HttpClient client) {
        this.client = client;
    }

    public HttpMethodBase getMethod() {
        return method;
    }

    public void setMethod(HttpMethodBase method) {
        this.method = method;
    }

    // [End] Getter,Setter method
}
